<?php

/**
 * @version     $Id: model.php 3 2010-10-08 17:49:35Z obalyuk87@gmail.com $
 * @package     AdminSafe
 * @subpackage  Components
 * @copyright   Copyright (C) 2010 Oleksandr Balyuk (www.boolcast.com)
 * @license     http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL v2
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
// No direct access
defined('_JEXEC') or die('Restricted access');
jimport('joomla.application.component.model');

/**
 * Description of accesslog
 *
 * @author Oleksandr Balyuk <obalyuk@boolcast.com>
 */
class AdminsafeModel extends JModel {

    /**
     * Items total
     * @var int
     */
    protected $_total = null;
    /**
     * Pagination object
     * @var JPagination
     */
    protected $_pagination = null;
    /**
     * Selected Data Holder
     *
     * @var array
     */
    protected $_data = null;
    /**
     * Default Order By Field
     * @var string
     */
    protected $_defaultOrderField = null;
    /**
     * Default Order By Direction
     * @var string
     */
    protected $_defaultOrderDir = 'ASC';
    /**
     * List of fields to select. key of the array field is used as an alias for the field
     * @var array
     */
    protected $_selectFields = array();
    /**
     * List of tables to select from, key of the array field is used as an alias for the table
     * @var array
     */
    protected $_fromTables = array();
    /**
     * List of complete where conditions (e.g. field_name = field_value)
     * @var array
     */
    protected $_whereConditions = array();
    /**
     * Adds join
     * @var array
     */
    protected $_joins = array();
    /**
     * List of filter fields
     * @var array
     */
    protected $_filterFields = array();
    /**
     * List of Group By Fields
     * @var array
     */
    protected $_groupByFields = array();

    /**
     * Default Constructor
     *
     * @param $config
     */
    function __construct($config = array()) {
        parent::__construct($config);

        //	Prepare States
        $this->prepareStates();
    }

    /**
     * Prepare States
     */
    protected function prepareStates() {

        $mainframe = & JFactory::getApplication();

        // Get pagination request variables
        $limit = $mainframe->getUserStateFromRequest('global.list.limit', 'limit', $mainframe->getCfg('list_limit'), 'int');
        $limitstart = JRequest::getInt('limitstart', 0);

        // In case limit has been changed, adjust it
        $limitstart = ($limit != 0 ? (floor($limitstart / $limit) * $limit) : 0);

        $this->setState('limit', $limit);
        $this->setState('limitstart', $limitstart);

        //
        $filter = $mainframe->getUserStateFromRequest("$this->_name.list.filter_search'", 'search', null, 'string');
        $this->setState('filter_search', $filter);


        //
        $this->setDefaultOrder($this->_defaultOrderField, $this->_defaultOrderDir);
    }

    /**
     * This function uses the _getListCount() method from JModel to return the total number of rows in the query.
     * The value returned will be used by the getPagination() function.
     *
     * @return int
     */
    protected function getTotal() {

        // Load the content if it doesn't already exist
        if (!$this->_total) {
            //	Get Query
            $query = $this->_buildCountQuery();

            //	Get Total
            $this->_db->setQuery($query);
            $this->_total = $this->_db->loadResult();

            //
            if ($this->_db->getError()) {
                $this->setError($this->_db->getError());
            }
        }

        return $this->_total;
    }

    /**
     * Build Complete Select Statement
     *
     * @return string select statement to get data
     */
    protected function _buildQuery() {

        //	Initialize
        $query = array();

        //
        $query[] = $this->_buildSelectClause();
        $query[] = $this->_buildFromClause();
        $query[] = $this->_buildWhereClause();
        $query[] = $this->_buildGroupByClause();
        $query[] = $this->_buildOrderByClause();

        //	Create Query
        $query = implode(' ', $query);

        //
        return $query;
    }

    /**
     * Build select clause. Uses _selectFields to generate select statement.
     * If _selectFields does not contain any fields wild card will be used (all fields will be selected)
     *
     * @return string
     */
    protected function _buildSelectClause() {
        //
        $query = '';

        if (count($this->_selectFields) > 0) {

            //	Create array that will hold individual fields for the select "fields AS alias"
            $fields = array();
            $pattern = '/^[A-Za-z][A-Za-z0-9_]*$/';

            //	Go through the list of fields and generate select statement
            foreach ($this->_selectFields as $alias => $field) {

                //	if $alias is specified and is alphanumeric and starts with a character
                if ($alias && preg_match($pattern, $alias)) {
                    $field .= " AS $alias";
                }
                //	Add to the list of fields
                $fields[] = $field;
            }

            //
            $query = implode(', ', $fields);
        }

        //	If  query is not set, select everything
        if (!$query) {
            //	Select All
            $query = 'SELECT *';
        } else {
            //	Select specified fields
            $query = "SELECT $query";
        }

        return $query;
    }

    /**
     * Buld Query to get total count
     *
     * @return string select statement to get total count
     */
    protected function _buildCountQuery() {

        //	Initialize
        $query = array();

        //
        $query[] = 'SELECT COUNT(*)';
        $query[] = $this->_buildFromClause();
        $query[] = $this->_buildWhereClause();
        $query[] = $this->_buildGroupByClause();


        //	Create Query
        $query = implode(' ', $query);

        //
        return $query;
    }

    /**
     * Buld From Clause
     *
     * @return string
     */
    protected function _buildFromClause() {

        //	From table must be present, otherwise throw exception
        if (count($this->_fromTables) == 0) {
            $class = __CLASS__;
            $line = __LINE__;
            throw new Exception("Invalid From Clause $class [$line]");
        }

        //
        $from = array();
        $pattern = '/^[A-Za-z][A-Za-z0-9_]*$/';


        //	Scan through the list of table and generate from clause
        foreach ($this->_fromTables as $alias => $table) {

            //	if $alias is specified and is alphanumeric and starts with a character
            if ($alias && preg_match($pattern, $alias)) {
                $table .= " AS $alias";
            }
            $from[] = $table;
        }

        //
        $from = implode(', ', $from);
        $from = "FROM $from";

        //  Check if any join conditions are present
        if (count($this->_joins) > 0) {
            $join = implode(" ", $this->_joins);
            $from .= " $join";
        }
        //
        return $from;
    }

    /**
     * Build Where Clause
     *
     * @return string
     */
    protected function _buildWhereClause() {

        //	Initialize
        $query = null;

        //
        $where = array();
        $where = array_merge($where, $this->_whereConditions);

        //	Get Filter Condition
        $filter = $this->_buildFilterCondition();
        if ($filter)
            $where[] = $filter;


        //	Create Where Clause
        $query = (count($where) == 0) ? null : 'WHERE ' . implode(' AND ', $where);

        //
        return $query;
    }

    /**
     * Build Filter condition
     *
     * @return string
     */
    protected function _buildFilterCondition() {

        //	Initialize
        $query = '';

        //	If Filter fields are specified
        if ((count($this->_filterFields) > 0)) {

            //	Get Filter
            $filter = $this->getState('filter_search');

            //	If Filter is set
            if ($filter) {
                //
                $query = array();

                //	Scan through all filter fields and generate filter conditions
                foreach ($this->_filterFields as $field) {
                    //	Create Filter Condition
                    $query[] = "$field LIKE {$this->_db->Quote("%$filter%")}";
                }

                //	Create Filter Condition
                $query = implode(' OR ', $query);
                $query = "($query)";
            }
        }

        //
        return $query;
    }

    /**
     * Build Group By Clause
     *
     * @return string
     */
    protected function _buildGroupByClause() {
        $query = '';

        //	If group by fields are specified
        if ($this->_groupByFields > 0) {
            $query = implode(', ', $this->_groupByFields);
        }

        //
        return $query;
    }

    /**
     * Build Order By Clause
     *
     * @return string
     */
    protected function _buildOrderByClause() {

        //
        $orderby = '';

        //	Get the Order Field
        $filter_order = $this->getState('filter_order');

        //	If Order Field is set, get the order direction and generate the order clause
        if ($filter_order) {

            //	Get Order Direction
            $filter_order_Dir = $this->getState('filter_order_Dir');

            //	Create Order By clause
            $orderby = "ORDER BY $filter_order $filter_order_Dir ";
        }

        return $orderby;
    }

    /**
     * Add Field to the list  of Select Fields
     *
     * @param string $fieldName field name
     * @param string $fieldAlias optional name of the alias
     * @return void
     */
    public function addSelectField($fieldName, $fieldAlias = null) {
        if (!$fieldName)
            return;

        //
        if ($fieldAlias) {
            //	Add field to array with key
            $this->_selectFields[$fieldAlias] = $fieldName;
        } else {
            if (!in_array($fieldName, $this->_selectFields)) {
                //	Add field to array without key
                $this->_selectFields[] = $fieldName;
            }
        }
    }

    /**
     * Add Table to the lisist of tables
     *
     * @param string $tableName
     * @param string $tableAlias
     * @return void
     */
    public function addTable($tableName, $tableAlias = null) {
        if (!$tableName)
            return;

        //
        if ($tableAlias) {
            //	Add with specified key
            $this->_fromTables[$tableAlias] = $tableName;
        } else {
            if (!in_array($tableName, $this->_fromTables)) {
                //	Add without specified key
                $this->_fromTables[] = $tableName;
            }
        }
    }

    /**
     * Adds Join
     * @param <type> $join
     * @return void
     */
    public function addJoin($join) {
        if (!$join)
            return;

        $this->_joins[] = $join;
    }

    /**
     * Add Condition
     *
     * @param unknown_type $condition
     * @return void
     */
    public function addWhereCondition($condition) {
        if (!$condition)
            return;

        if (!in_array($condition, $this->_whereConditions)) {
            //	Add Condition to list
            $this->_whereConditions[] = $condition;
        }
    }

    /**
     * Add Filter Field
     *
     * @param string $fieldName
     * @return void
     */
    public function addFilterField($fieldName) {
        if (!$fieldName)
            return;

        if (!in_array($fieldName, $this->_filterFields)) {
            //	Add Filter field
            $this->_filterFields[] = $fieldName;
        }
    }

    /**
     * Add Group By Field
     * @param string $fieldName name of the field to group by
     * @return void
     */
    public function addGroupBy($fieldName) {
        if (!$fieldName)
            return;

        //
        if (!in_array($fieldName, $this->_groupByFields)) {
            $this->_groupByFields[] = $fieldName;
        }
    }

    /**
     * Set Default Order
     *
     * @param string $field field name for default order
     * @param string $direction ASC/DESC, order direction
     * @return void
     */
    public function setDefaultOrder($field, $direction = 'ASC') {
        $mainframe = & JFactory::getApplication();

        //
        $filter_order = $mainframe->getUserStateFromRequest("$this->_name.list.filter_order'", 'filter_order', $field, 'cmd');
        $filter_order_Dir = $mainframe->getUserStateFromRequest("$this->_name.list.filter_order_Dir", 'filter_order_Dir', $direction, 'word');

        //
        $this->setState('filter_order', $filter_order);
        $this->setState('filter_order_Dir', $filter_order_Dir);
    }

    /**
     * Get Data
     *
     * @return array returns list of objects
     */
    public function &getData() {

        //
        if (!$this->_data) {

            //	Build Query
            $query = $this->_buildQuery();

            //	Load Data
            $this->_data = & $this->_getList($query, $this->getState('limitstart'), $this->getState('limit'));

            //
            if ($this->_db->getError()) {
                $this->setError($this->_db->getError());
            }
        }

        //
        return $this->_data;
    }

    /**
     * Gets first row in the data list
     *
     * @return object
     */
    public function &getDataRow() {

        //  Get Data
        $data = & $this->getData();

        //  Check Data, if ok return first row, otherwise return NULL
        if (is_object($data)) {
            return $data;
        } elseif (is_array($data) && count($data) > 0) {
            return $data[0];
        } else {
            $ret = null;
            return $ret;
        }
    }

    /**
     * The function will create and return a new Pagination object that can be accessed by the View.
     *
     * @return JPagination
     */
    public function &getPagination() {

        // Load the content if it doesn't already exist
        if (empty($this->_pagination)) {
            jimport('joomla.html.pagination');
            $this->_pagination = new JPagination($this->getTotal(), $this->getState('limitstart'), $this->getState('limit'));
        }

        return $this->_pagination;
    }

    /**
     * Gets Lists
     *
     * @return array
     */
    public function &getLists() {
        $lists = array();

        //
        $lists['order_Dir'] = $this->getState('filter_order_Dir');
        $lists['order'] = $this->getState('filter_order');
        $lists['search'] = $this->getState('filter_search');
        $lists['limit'] = $this->getState('limit');
        $lists['limitstart'] = $this->getState('limitstart');

        return $lists;
    }

    /**
     * Publish
     *
     * @param array $cids array with ids
     * @return bool True on success
     */
    public function publish($cids) {
        //
        JArrayHelper::toInteger($cids);
        return $this->_changePublishStatus($cids);
    }

    /**
     * Unpublish
     *
     * @param array $cids array with ids
     * @return bool True on success
     */
    public function unpublish($cids) {
        //
        JArrayHelper::toInteger($cids);
        return $this->_changePublishStatus($cids, false);
    }

    /**
     * Archives
     *
     * @param array $cids
     * @return bool True on success
     */
    public function archive($cids) {
        //
        JArrayHelper::toInteger($cids);
        return $this->_changeArchiveStatus($cids, true);
    }

    /**
     * Unarchives
     *
     * @param array $cids
     * @return bool True on success
     */
    public function unarchive($cids) {
        //
        JArrayHelper::toInteger($cids);
        return $this->_changeArchiveStatus($cids, false);
    }

    /**
     * Method to delete record(s)
     *
     * @param array $cids array with ids
     * @return bool True on success
     */
    public function delete($cids) {
        //
        JArrayHelper::toInteger($cids);
        return $this->_delete($cids);
    }

    /**
     * Deletes specified records
     *
     * @param array $cids
     * @return bool
     */
    protected function _delete($cids) {

        //
        if (is_array($cids)) {
            //
            $tbl = & $this->getTable();
            foreach ($cids as $cid) {
                if ($tbl->canDelete($cid)) {
                    if (!$tbl->delete($cid)) {
                        $this->setError($tbl->getError());
                        return false;
                    }
                } else {
                    if ($tbl->getError()) {
                        $this->setError($tbl->getError());
                    }
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Changes the archive status of the records
     *
     * @param array $cids
     * @param bool $archive
     * @return bool True on success
     */
    protected function _changeArchiveStatus($cids, $archive = true) {
        //
        if (is_array($cids)) {
            $tbl = $this->getTable();
            if (!$tbl->archive($cids, $archive)) {
                JError::raiseWarning(500, $tbl->getError());
                return false;
            }
            return true;
        }
        return false;
    }

    /**
     * Changes published status of the records.
     *
     * @param array $cids array with ids
     * @param bool $publish new status
     * @return bool True on success
     */
    protected function _changePublishStatus($cids, $publish = true) {
        //
        if (is_array($cids)) {
            $tbl = $this->getTable();
            if (!$tbl->publish($cids, $publish)) {
                JError::raiseWarning(500, $tbl->getError());
                return false;
            }
            return true;
        }
        return false;
    }

}